package com.demozz.circle_ratio;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 圆周率计算
 *
 */
public class App {
	public static void main(String[] args) {
		int scale = 300;// 300位精度耗时9秒
		Long t1 = System.currentTimeMillis();
		BigDecimal piValue = circleRadioCalc(scale);
		Long t2 = System.currentTimeMillis();
		String pi = format(piValue, scale);
		System.out.println("pi =\n" + pi);
		System.out.println("执行时间:" + now());
		System.out.println("耗时:" + (t2 - t1));
	}

	/**
	 * 格式化输出，每10位换一行
	 * 
	 * @param num
	 * @param scale
	 * @return
	 */
	private static String format(BigDecimal num, int scale) {
		String str = num.toString();
		// 去掉最后若干位不精确的数字
		str = str.substring(0, str.length() - 5);
		String result = str.substring(0, 2) + "\n";
		int len = str.length();
		len = len - 2;
		int size = len / 10;
		for (int i = 0; i < size; i++) {
			result += str.substring(i * 10 + 2, (i + 1) * 10 + 2) + "\n";
		}
		if (len % 10 != 0) {
			result += str.substring(size * 10 + 2) + "\n";
		}
		return result;
	}

	private static String now() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(new Date());
	}

	private static BigDecimal circleRadioCalc(int scale) {
		BigDecimal num1 = new BigDecimal("1");
		BigDecimal num4 = new BigDecimal("4");
		BigDecimal num5 = new BigDecimal("5");
		BigDecimal num239 = new BigDecimal("239");
		BigDecimal fraction1 = divide(num1, num5, scale);
		BigDecimal fraction2 = divide(num1, num239, scale);
		BigDecimal angle1 = arctan(fraction1, scale);
		BigDecimal jiaodu2 = arctan(fraction2, scale);
		BigDecimal term1 = num4.multiply(angle1);
		BigDecimal term2 = num1.multiply(jiaodu2);
		BigDecimal reciprocal = term1.subtract(term2);
		return num4.multiply(reciprocal);
	}

	private static BigDecimal divide(BigDecimal num1, BigDecimal num2, int scale) {
		BigDecimal result = num1.divide(num2, scale, RoundingMode.HALF_UP);
		return result;
	}

	private static BigDecimal arctan(BigDecimal num, int scale) {
		BigDecimal numx = new BigDecimal(num + "");
		BigDecimal numall = new BigDecimal(num + "");
		for (int i = 0; i < scale; i++) {
			int exponent = i * 2 + 3;
			BigDecimal factorialNum = new BigDecimal(exponent + "");
			BigDecimal pow = numx.pow(exponent);
			BigDecimal subtrahend = pow.divide(factorialNum, scale, RoundingMode.HALF_UP);
			if (i % 2 == 0) {
				numall = numall.subtract(subtrahend);
			} else {
				numall = numall.add(subtrahend);
			}
		}
		return numall;
	}
}
